package com.xuyuan.experssion.ognl;

import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.Data;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import org.aspectj.lang.annotation.Before;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class OGNLExam {
    @Test
    public void test() throws OgnlException {
        // 1.创建Ognl上下文(本质是map,有一个特殊的key用来放root,普通key直接put)
        // 2.构建表达式
        // 访问root对象相关(不以#开头表示直接访问根对象)
        Object rootExp1 = Ognl.parseExpression("orderNo");
        Object rootExp2 = Ognl.parseExpression("address.city");
        // 访问普通对象相关(以#开头表示先去普通对象找,如果找不到再去根对象中去找)
        Object normalExp1 = Ognl.parseExpression("#key1");
        Object normalExp2 = Ognl.parseExpression("#key2");
        // 静态方法调用
        Object methodCallExp = Ognl.parseExpression("@java.math.BigDecimal@valueOf(12)");

        // 3.将要解析的对象及根对象放入Ognl上下文(如果是静态方法调用可以不放)
        // 放入根对象
        context.setRoot(buildOrder());
        // 放入普通对象
        context.put("key1", "val1");
        context.put("key2", "val2");

        // 4.获取对应属性或调用方法
        Object rootExpVal1 = Ognl.getValue(rootExp1, context, context.getRoot());
        System.out.println("rootExpVal1:" + rootExpVal1);
        Object rootExpVal2 = Ognl.getValue(rootExp2, context, context.getRoot());
        System.out.println("rootExpVal2:" + rootExpVal2);
        Object normalExpVal1 = Ognl.getValue(normalExp1, context, context.getRoot());
        System.out.println("normalExpVal1:" + normalExpVal1);
        Object normalExpVal2 = Ognl.getValue(normalExp2, context, context.getRoot());
        System.out.println("normalExpVal2:" + normalExpVal2);
        Object methodCallExpVal = Ognl.getValue(methodCallExp, context, context.getRoot());
        System.out.println("methodCallExpVal:" + methodCallExpVal);

    }

    OgnlContext ognlContext;
    OgnlContext context;

    @Before("")
    public void before() {
        ognlContext = Ognl.createDefaultContext(buildOrder());
        context = Ognl.createDefaultContext(null);
    }

    @Test
    public void longEquals() throws OgnlException {
        Object value = Ognl.getValue("buyer == 1160397", ognlContext, ognlContext.getRoot());
        Assert.isTrue((Boolean) value);
        Object value2 = Ognl.getValue("buyer == 1160397L", ognlContext, ognlContext.getRoot());
        Assert.isTrue((Boolean) value2);
    }

    @Test
    public void strEquals() throws OgnlException {
        Object value = Ognl.getValue("comment == '请务必发顺丰'", ognlContext, ognlContext.getRoot());
        Assert.isTrue((Boolean) value);
        Object value2 = Ognl.getValue("comment.equals('请务必发顺丰')", ognlContext, ognlContext.getRoot());
        Assert.isTrue((Boolean) value2);
    }

    /**
     * 基于常量构建
     */
    @Test
    public void generate() throws OgnlException {
        // 调用new创建
        Object newList = Ognl.getValue("new java.util.ArrayList()", null);
        System.out.printf("newList,class:%s,toString:%s%n", newList.getClass(), JSON.toJSONString(newList));
        // 通过json格式反序列化创建
        System.out.println(Ognl.getValue("{}", (Object) null, ArrayList.class));
        Object deserializeList = Ognl.getValue("{'aa', 'bb', 'cc', 'dd'}", (Object) null, ArrayList.class);
        System.out.printf("deserializeList,class:%s,toString:%s%n", deserializeList.getClass(), JSON.toJSONString(deserializeList));
        Object buildAndIndex = Ognl.getValue("{'aa', 'bb', 'cc', 'dd'}[2]", null);
        System.out.println("buildAndIndex:" + buildAndIndex);
        // 通过Lists工具类创建
        Object guavaLists = Ognl.getValue("@com.google.common.collect.Lists@newArrayList('aa', 'bb', 'cc', 'dd')", (Object) null, ArrayList.class);
        System.out.printf("guavaLists,class:%s,toString:%s%n", guavaLists.getClass(), JSON.toJSONString(guavaLists));
    }

    /**
     * 构建的元素可以基于方法调用或现有上下文构建
     */
    @Test
    public void generate2() throws OgnlException {
        Object value = Ognl.getValue("{comment,new java.util.Random().nextInt(10),orderNo,items}", ognlContext, ognlContext.getRoot());
        System.out.println(value);
    }

    /**
     * 相当于集合的stream.map.collectToList,返回值是ArrayList
     * 格式: collection.{? 过滤条件},过滤条件中将元素作为root的表达式
     */
    @Test
    public void filter1() throws OgnlException {
        ognlContext.put("strList", Lists.newArrayList("a", "bb", "ccc", "dddd", "eeeee"));
        // 基本类型集合
        Object filterList = Ognl.getValue("#strList.{? length() > 2}", ognlContext, buildOrder());
        System.out.println("filterList:" + JSON.toJSONString(filterList));
        // 对象集合
        Object filterList2 = Ognl.getValue("items.{? quantity > 2}", ognlContext, buildOrder());
        System.out.println("filterList2:" + JSON.toJSONString(filterList2));
    }

    /**
     * 过滤后取满足条件的第一个
     * 格式：collection.{^ 过滤条件},过滤条件中将元素作为root的表达式
     */
    @Test
    public void filterAndPeekFirst1() throws OgnlException {
        ognlContext.put("strList", Lists.newArrayList("a", "bb", "ccc", "dddd", "eeeee"));
        // 基本类型集合
        Object filterList = Ognl.getValue("#strList.{^ length() > 2}", ognlContext, buildOrder());
        System.out.println("filterList:" + JSON.toJSONString(filterList));
        // 对象集合
        Object filterList2 = Ognl.getValue("items.{^ quantity > 2}", ognlContext, buildOrder());
        System.out.println("filterList2:" + JSON.toJSONString(filterList2));
    }

    /**
     * 过滤后取满足条件的最后一个
     * 格式：collection.{^ 过滤条件},过滤条件中将元素作为root的表达式
     */
    @Test
    public void filterAndPeekLast1() throws OgnlException {
        ognlContext.put("strList", Lists.newArrayList("a", "bb", "ccc", "dddd", "eeeee"));
        // 基本类型集合
        Object filterList = Ognl.getValue("#strList.{$ length() > 2}", ognlContext, buildOrder());
        System.out.println("filterList:" + JSON.toJSONString(filterList));
        // 对象集合
        Object filterList2 = Ognl.getValue("items.{$ quantity > 2}", ognlContext, buildOrder());
        System.out.println("filterList2:" + JSON.toJSONString(filterList2));
    }

    /**
     * 相当于集合的stream.map.collectToList,返回值是ArrayList
     * 格式：collection.{function},过滤条件中将元素作为root的表达式
     */
    @Test
    public void mapToCollection1() throws OgnlException {
        ognlContext.put("strList", Lists.newArrayList("a", "bb", "ccc", "dddd", "eeeee"));
        // 转化
        Object mapList = Ognl.getValue("#strList.{length()}", ognlContext, buildOrder());
        System.out.println("mapList:" + JSON.toJSONString(mapList));
    }

    /**
     * 获取元素
     */
    @Test
    public void getByIndex() throws OgnlException {
        Order order = buildOrder();
        Item item2 = (Item) Ognl.getValue("items[1]", order);
        System.out.println("item2:" + JSON.toJSONString(item2));
        Item item1 = (Item) Ognl.getValue("items.get(0)", order);
        System.out.println("item1:" + JSON.toJSONString(item1));
    }

    /**
     * 调用List的方法
     */
    @Test
    public void callListMethod() throws OgnlException {
        Order order = buildOrder();
        Object size = Ognl.getValue("items.size()", order);
        System.out.println("size:" + size);

        Object isEmpty = Ognl.getValue("items.isEmpty()", order);
        System.out.println("isEmpty:" + isEmpty);

        Object removeResult = Ognl.getValue("items.remove(0)", order);
        System.out.println("removeResult:" + removeResult + ",after size:" + order.getItems().size());

        Object clearResult = Ognl.getValue("items.clear()", order);
        System.out.println("clearResult:" + clearResult + ",after size:" + order.getItems().size());
    }

    /**
     * 替换元素
     */
    @Test
    public void replaceElement() throws OgnlException {
        Order order = buildOrder();
        Ognl.setValue("items[0]", order, buildItem(6));
        System.out.println("afterValue:" + order.getItems().get(0));
    }

    /**
     * 构建Set
     */
    @Test
    public void generateSet() throws OgnlException {
        // 调用new创建
        Object newSet = Ognl.getValue("new java.util.HashSet()", null);
        System.out.printf("newSet,class:%s,toString:%s%n", newSet.getClass(), JSON.toJSONString(newSet));
        // 不能通过json格式反序列化创建
        // 能通过Sets工具类创建
        Object guavaSets = Ognl.getValue("@com.google.common.collect.Sets@newHashSet('aa', 'bb', 'cc', 'dd')", (Object) null, HashSet.class);
        System.out.printf("guavaSets,class:%s,toString:%s%n", guavaSets.getClass(), JSON.toJSONString(guavaSets));
    }


    /**
     * 相当于集合的stream.map.collectToList,返回值是ArrayList
     * 格式: collection.{? 过滤条件},过滤条件中将元素作为root的表达式
     */
    @Test
    public void filter() throws OgnlException {
        Object filterList = Ognl.getValue("tags.{? intValue() > 1}", ognlContext, ognlContext.getRoot());
        System.out.printf("filterSet,class:%s,collection:%s%n", filterList.getClass(), JSON.toJSONString(filterList));
    }

    /**
     * 过滤后取满足条件的第一个
     * 格式：collection.{^ 过滤条件},过滤条件中将元素作为root的表达式
     */
    @Test
    public void filterAndPeekFirst() throws OgnlException {
        Object filterSet = Ognl.getValue("tags.{^ intValue() > 1}", ognlContext, ognlContext.getRoot());
        System.out.printf("filterSet,class:%s,collection:%s%n", filterSet.getClass(), JSON.toJSONString(filterSet));
    }

    /**
     * 过滤后取满足条件的最后一个
     * 格式：collection.{^ 过滤条件},过滤条件中将元素作为root的表达式
     */
    @Test
    public void filterAndPeekLast() throws OgnlException {
        Object filterSet = Ognl.getValue("tags.{$ intValue() > 1}", ognlContext, ognlContext.getRoot());
        System.out.printf("filterSet,class:%s,collection:%s%n", filterSet.getClass(), JSON.toJSONString(filterSet));
    }

    /**
     * 相当于集合的stream.map.collectToList,返回值是ArrayList
     * 格式：collection.{function},过滤条件中将元素作为root的表达式
     */
    @Test
    public void mapToCollection() throws OgnlException {
        Object filterSet = Ognl.getValue("tags.{intValue()+1}", ognlContext, ognlContext.getRoot());
        System.out.printf("filterSet,class:%s,collection:%s%n", filterSet.getClass(), JSON.toJSONString(filterSet));
    }

    /**
     * 常调用的方法
     */
    @Test
    public void callSetMethod() throws OgnlException {

        Order order = buildOrder();
        Object size = Ognl.getValue("items.size()", order);
        System.out.println("size:" + size);

        Object isEmpty = Ognl.getValue("items.isEmpty()", order);
        System.out.println("isEmpty:" + isEmpty);

        Object clearResult = Ognl.getValue("items.clear()", order);
        System.out.println("clearResult:" + clearResult + ",after size:" + order.getItems().size());
    }

    /**
     * 构建Map
     */
    @Test
    public void generateMap() throws OgnlException {

        // 调用new创建
        Object newMap = Ognl.getValue("new java.util.HashMap()", null);
        System.out.printf("newMap,class:%s,toString:%s%n", newMap.getClass(), JSON.toJSONString(newMap));
        // 通过json格式反序列化创建
        Object deserializeMap = Ognl.getValue("#{'k1':'v1','k2':'v2'}", (Object) null, HashMap.class);
        System.out.printf("deserializeMap,class:%s,toString:%s%n", deserializeMap.getClass(), JSON.toJSONString(deserializeMap));
        Object buildAndIndex = Ognl.getValue("#{'k1':'v1','k2':'v2'}['k1']", null);
        System.out.println("buildAndIndex:" + buildAndIndex);
    }

    /**
     * 获取元素方法:
     * 1.当成对象使用,key就是属性
     * 2.当前map使用,调用get(key)方法
     */
    @Test
    public void getByKey() throws OgnlException {

        Order order = buildOrder();
        Object val1 = Ognl.getValue("orderContext.device", order);
        System.out.println("orderContext.device:" + val1);
        Object val11 = Ognl.getValue("orderContext.get(\"device\")", order);
        System.out.println("orderContext.device:" + val11);
        Object val2 = Ognl.getValue("orderContext.source.orderFrom", order);
        System.out.println("orderContext.source.orderFrom:" + val2);
        Object val22 = Ognl.getValue("orderContext.get(\"source\").get(\"orderFrom\")", order);
        System.out.println("orderContext.source.orderFrom:" + val22);
    }

    /**
     * 调用Map的方法
     */
    @Test
    public void callMapMethod() throws OgnlException {

        Order order = buildOrder();
        Object size = Ognl.getValue("orderContext.size()", order);
        System.out.println("size:" + size);

        Object isEmpty = Ognl.getValue("orderContext.isEmpty()", order);
        System.out.println("isEmpty:" + isEmpty);

        Object containsKey = Ognl.getValue("orderContext.containsKey(\"OrderFrom\")", order);
        System.out.println("containsKey:" + containsKey);

        Object keySet = Ognl.getValue("orderContext.keySet()", order);
        System.out.println("keySet:" + keySet);

        Object values = Ognl.getValue("orderContext.values()", order);
        System.out.println("values:" + values);

        Object putResult = Ognl.getValue("orderContext.put(\"new\",\"val\")", order);
        System.out.println("putResult:" + putResult + ",after keySets:" + order.getOrderContext().keySet());

        Object removeResult = Ognl.getValue("orderContext.remove(\"new\")", order);
        System.out.println("removeResult:" + removeResult + ",after keySets:" + order.getOrderContext().keySet());

        Object clearResult = Ognl.getValue("orderContext.clear()", order);
        System.out.println("clearResult:" + clearResult + ",after size:" + order.getOrderContext().size());
    }

    /**
     * 构建数组
     */
    @Test
    public void generateArray() throws OgnlException {

        // 调用new创建
        Object newArr = Ognl.getValue("new String[5]", null);
        System.out.printf("newArr,class:%s,toString:%s%n", newArr.getClass(), JSON.toJSONString(newArr));
        // 通过匿名调用构建
        Object newArr2 = Ognl.getValue("new String[]{'aa', 'bb', 'cc', 'dd'}", null);
        System.out.printf("newArr,class:%s,toString:%s%n", newArr2.getClass(), JSON.toJSONString(newArr2));
        // 不能通过json格式反序列化创建
    }

    /**
     * 获取元素
     */
    @Test
    public void getByIndex1() throws OgnlException {

        Order order = buildOrder();
        Object promotionTypes2 = Ognl.getValue("promotionTypes[1]", order);
        System.out.println("promotionTypes2:" + promotionTypes2);
    }

    /**
     * 获取length
     */
    @Test
    public void length() throws OgnlException {
        Order order = buildOrder();
        Object length = Ognl.getValue("promotionTypes.length", order);
        System.out.println("length:" + length);
    }

    /**
     * 静态方法调用,格式为：@class@method(params)
     * 1.class必须为全路径
     * 2.可以有参数,也可以无参数,根据方法实际情况
     * 3.如果表达式很复杂可以调用自己写的static方法(参数简化为基本类型)
     */
    @Test
    public void callStaticMethod() throws OgnlException {
        BigDecimal bigDecimal = (BigDecimal) Ognl.getValue("@java.math.BigDecimal@valueOf(12)", null);
        System.out.println("bigDecimal:" + bigDecimal);

        Object random = Ognl.getValue("@java.lang.Math@random()", null);
        System.out.println("random:" + random);
    }

    /**
     * 静态属性获取,格式为：@class@field
     * class必须为全路径
     */
    @Test
    public void staticField() throws OgnlException {
        System.out.println(Ognl.getValue("@java.lang.System@out", null));
        Ognl.getValue("@java.lang.System@out.print(\"print\")", null);
    }

    /**
     * 条件
     */
    @Test
    public void and() throws OgnlException {
        Object and1 = Ognl.getValue("items.size() > 10 and tags.size() > 2", ognlContext, ognlContext.getRoot());
        System.out.println("and1:" + and1);
        Object and2 = Ognl.getValue("items.size() > 10 && tags.size() > 2", ognlContext, ognlContext.getRoot());
        System.out.println("and2:" + and2);

    }

    @Test
    public void or() throws OgnlException {
        Object or = Ognl.getValue("items.size() > 10 or tags.size() > 2", ognlContext, ognlContext.getRoot());
        System.out.println("or:" + or);
        Object or2 = Ognl.getValue("items.size() > 10 || tags.size() > 2", ognlContext, ognlContext.getRoot());
        System.out.println("or2:" + or2);
    }

    /**
     * 内部表达式用括号括起来
     */
    @Test
    public void not() throws OgnlException {
        System.out.println(Ognl.getValue("!(items.size() > 10)", ognlContext, ognlContext.getRoot()));
        System.out.println(Ognl.getValue("not (items.size() > 10)", ognlContext, ognlContext.getRoot()));
    }

    @Test
    public void instanceOf() throws OgnlException {
        Object instanceOf = Ognl.getValue("address instanceof Object", ognlContext, ognlContext.getRoot());
        System.out.println("instanceOf:" + instanceOf);
    }

    /**
     * 判断元素是否在集合或数组当中,一般用户基本元素
     */
    @Test
    public void in() throws OgnlException {
        System.out.println(Ognl.getValue("1 in tags", ognlContext, ognlContext.getRoot()));
        System.out.println(Ognl.getValue("'VIP' in promotionTypes", ognlContext, ognlContext.getRoot()));
        System.out.println(Ognl.getValue("1 in items.{quantity}", ognlContext, ognlContext.getRoot()));
    }

    @Test
    public void compare() throws OgnlException {
        context.put("num", 10);

        Assert.isTrue((Boolean) Ognl.getValue("#num == 10", context, context.getRoot()));
        Assert.isTrue((Boolean) Ognl.getValue("#num eq 10", context, context.getRoot()));
        Assert.isTrue((Boolean) Ognl.getValue("#num != 9", context, context.getRoot()));
        Assert.isTrue((Boolean) Ognl.getValue("#num neq 9", context, context.getRoot()));

        Assert.isTrue((Boolean) Ognl.getValue("#num > 0", context, context.getRoot()));
        Assert.isTrue((Boolean) Ognl.getValue("#num gt 0", context, context.getRoot()));
        Assert.isTrue((Boolean) Ognl.getValue("#num >= 0", context, context.getRoot()));
        Assert.isTrue((Boolean) Ognl.getValue("#num gte 0", context, context.getRoot()));

        Assert.isFalse((Boolean) Ognl.getValue("#num < 0", context, context.getRoot()));
        Assert.isFalse((Boolean) Ognl.getValue("#num lt 0", context, context.getRoot()));
        Assert.isFalse((Boolean) Ognl.getValue("#num <= 0", context, context.getRoot()));
        Assert.isFalse((Boolean) Ognl.getValue("#num lte 0", context, context.getRoot()));
    }

    /**
     * 执行表达式列表：
     * 1.赋值value1到上下文中(获取系统变量1),表达式为静态方法调用
     * 2.赋值value2到上下文中(获取系统变量2),表达式为静态方法调用
     * 3.构建一个集合,元素为上下文中的value1,value2
     */
    @Test
    public void multiGetByStaticMethod() throws OgnlException {
        Object value = Ognl.getValue("#value1=@System@getProperty(\"java.home\"), #value2=@System@getProperty(\"java.runtime.name\"),{#value1, #value2}", null);
        System.out.println(value);
        Object value2 = Ognl.getValue("{@java.lang.System@getProperty(\"java.home\"),@java.lang.System@getProperty(\"java.runtime.name\")}", null);
        System.out.println(value2);
    }

    /**
     * 基于上下文的对象执行多个获取表达式,并将构成集合返回
     * 这里其他是一个表达式, 构建集合(集合的每个元素分别对应一个表达式)
     */
    @Test
    public void multiGet() throws OgnlException {
        Object value = Ognl.getValue("{items[0].itemName,comment,orderNo.length(),1+1}", ognlContext, ognlContext.getRoot());
        System.out.println(value);
    }

    /**
     * 执行表达式列表：
     * 1.上下文中的对象操作1
     * 2.上下文中的对象操作2
     * 3.上下文中的对象操作3
     * 4.根据表达式上下文中解析
     * 第4个表达式的返回值作为这个返回值
     */
    @Test
    public void multiInvoke() throws OgnlException {
        Object value = Ognl.getValue("items.remove(0),address.setMobile(\"17511112222\"),tags.clear(),tags.size()", ognlContext, ognlContext.getRoot());
        System.out.println(value);
    }

    @Test
    public void base() throws OgnlException {
        System.out.println(Ognl.getValue("4 | 2", ognlContext, ognlContext.getRoot()));
        System.out.println(Ognl.getValue("4 & 2", ognlContext, ognlContext.getRoot()));
        System.out.println(Ognl.getValue("4 xor 2", ognlContext, ognlContext.getRoot()));
        // 左移 shipLeft
        System.out.println(Ognl.getValue("8 << 1", ognlContext, ognlContext.getRoot()));
        System.out.println(Ognl.getValue("8 shl 1", ognlContext, ognlContext.getRoot()));
        // 右边移 shipRight
        System.out.println(Ognl.getValue("8 >> 1", ognlContext, ognlContext.getRoot()));
        System.out.println(Ognl.getValue("8 shr 1", ognlContext, ognlContext.getRoot()));
        // 无条件右移 unlimited shipRight
        System.out.println(Ognl.getValue("8 >>> 1", ognlContext, ognlContext.getRoot()));
        System.out.println(Ognl.getValue("8 ushr 1", ognlContext, ognlContext.getRoot()));
    }


    @Data
    static class Order {

        private String orderNo;
        private String comment;
        private Long buyer;
        private BigDecimal payAmt;
        private Address address;
        private List<Item> items;
        private Set<Integer> tags;
        private String[] promotionTypes;
        private Map<String, Object> orderContext;

        public static Order parse(String str) {
            return JSON.parseObject(str, Order.class);
        }
    }


    @Data
    static class Item {
        private String itemName;
        private Long itemId;
        private Long quantity;
    }

    @Data
    static class Address {
        private String province;
        private String city;
        private String zone;
        private String mobile;
        private String name;
    }

    public static Order buildOrder() {
        Order order = new Order();
        order.setOrderNo("202306010013600221");
        order.setBuyer(1160397L);
        order.setComment("请务必发顺丰");
        order.setPayAmt(new BigDecimal("12.0"));
        order.setTags(Sets.newHashSet(1, 2, 3));
        order.setPromotionTypes(new String[]{"VIP", "COUPON", "FULL_REDUCE"});
        order.setAddress(buildAddress());
        order.setItems(buildItems());
        order.setOrderContext(buildContext());
        return order;
    }

    private static Address buildAddress() {
        Address address = new Address();
        address.setProvince("上海市");
        address.setCity("上海市");
        address.setZone("浦东新区");
        address.setMobile("17611112222");
        address.setName("Mr.Wang");
        return address;
    }

    private static List<Item> buildItems() {

        List<Item> items = new ArrayList<>();
        for (int i = 1; i <= 5; i++) {

            Item item = new Item();
            item.setItemId(10117000120L + i);
            item.setItemName("香草冰淇淋" + i);
            item.setQuantity((long) i);
            items.add(item);
        }
        return items;
    }

    protected static Item buildItem(int i) {

        Item item = new Item();
        item.setItemId(10117000120L + i);
        item.setItemName("香草冰淇淋" + i);
        item.setQuantity((long) i);
        return item;
    }

    private static Map<String, Object> buildContext() {
        Map<String, Object> map = new HashMap<>();
        map.put("device", "H5");
        Map<String, String> source = new HashMap<>();
        source.put("orderFrom", "1");
        source.put("distId", "1160397");
        map.put("source", source);
        map.put("expire", 3600);
        return map;
    }


}
